home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / p_client.c1 < prev    next >
Encoding:
Text File  |  2002-12-27  |  36.5 KB  |  1,587 lines

  1. #include "g_local.h"
  2. #include "m_player.h"
  3.  
  4. void ClientUserinfoChanged (edict_t *ent, char *userinfo);
  5.  
  6. void SP_misc_teleporter_dest (edict_t *ent);
  7.  
  8. //
  9. // Gross, ugly, disgustuing hack section
  10. //
  11.  
  12. // this function is an ugly as hell hack to fix some map flaws
  13. //
  14. // the coop spawn spots on some maps are SNAFU.  There are coop spots
  15. // with the wrong targetname as well as spots with no name at all
  16. //
  17. // we use carnal knowledge of the maps to fix the coop spot targetnames to match
  18. // that of the nearest named single player spot
  19.  
  20. static void SP_FixCoopSpots (edict_t *self)
  21. {
  22.     edict_t    *spot;
  23.     vec3_t    d;
  24.  
  25.     spot = NULL;
  26.  
  27.     while(1)
  28.     {
  29.         spot = G_Find(spot, FOFS(classname), "info_player_start");
  30.         if (!spot)
  31.             return;
  32.         if (!spot->targetname)
  33.             continue;
  34.         VectorSubtract(self->s.origin, spot->s.origin, d);
  35.         if (VectorLength(d) < 384)
  36.         {
  37.             if ((!self->targetname) || stricmp(self->targetname, spot->targetname) != 0)
  38.             {
  39. //                gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname);
  40.                 self->targetname = spot->targetname;
  41.             }
  42.             return;
  43.         }
  44.     }
  45. }
  46.  
  47. // now if that one wasn't ugly enough for you then try this one on for size
  48. // some maps don't have any coop spots at all, so we need to create them
  49. // where they should have been
  50.  
  51. static void SP_CreateCoopSpots (edict_t *self)
  52. {
  53.     edict_t    *spot;
  54.  
  55.     if(stricmp(level.mapname, "security") == 0)
  56.     {
  57.         spot = G_Spawn();
  58.         spot->classname = "info_player_coop";
  59.         spot->s.origin[0] = 188 - 64;
  60.         spot->s.origin[1] = -164;
  61.         spot->s.origin[2] = 80;
  62.         spot->targetname = "jail3";
  63.         spot->s.angles[1] = 90;
  64.  
  65.         spot = G_Spawn();
  66.         spot->classname = "info_player_coop";
  67.         spot->s.origin[0] = 188 + 64;
  68.         spot->s.origin[1] = -164;
  69.         spot->s.origin[2] = 80;
  70.         spot->targetname = "jail3";
  71.         spot->s.angles[1] = 90;
  72.  
  73.         spot = G_Spawn();
  74.         spot->classname = "info_player_coop";
  75.         spot->s.origin[0] = 188 + 128;
  76.         spot->s.origin[1] = -164;
  77.         spot->s.origin[2] = 80;
  78.         spot->targetname = "jail3";
  79.         spot->s.angles[1] = 90;
  80.  
  81.         return;
  82.     }
  83. }
  84.  
  85.  
  86. /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
  87. The normal starting point for a level.
  88. */
  89. void SP_info_player_start(edict_t *self)
  90. {
  91.     if (!coop->value)
  92.         return;
  93.     if(stricmp(level.mapname, "security") == 0)
  94.     {
  95.         // invoke one of our gross, ugly, disgusting hacks
  96.         self->think = SP_CreateCoopSpots;
  97.         self->nextthink = level.time + FRAMETIME;
  98.     }
  99. }
  100.  
  101. /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32)
  102. potential spawning position for deathmatch games
  103. */
  104. void SP_info_player_deathmatch(edict_t *self)
  105. {
  106.     if (!deathmatch->value)
  107.     {
  108.         G_FreeEdict (self);
  109.         return;
  110.     }
  111.     SP_misc_teleporter_dest (self);
  112. }
  113.  
  114. /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32)
  115. potential spawning position for coop games
  116. */
  117.  
  118. void SP_info_player_coop(edict_t *self)
  119. {
  120.     if (!coop->value)
  121.     {
  122.         G_FreeEdict (self);
  123.         return;
  124.     }
  125.  
  126.     if((stricmp(level.mapname, "jail2") == 0)   ||
  127.        (stricmp(level.mapname, "jail4") == 0)   ||
  128.        (stricmp(level.mapname, "mine1") == 0)   ||
  129.        (stricmp(level.mapname, "mine2") == 0)   ||
  130.        (stricmp(level.mapname, "mine3") == 0)   ||
  131.        (stricmp(level.mapname, "mine4") == 0)   ||
  132.        (stricmp(level.mapname, "lab") == 0)     ||
  133.        (stricmp(level.mapname, "boss1") == 0)   ||
  134.        (stricmp(level.mapname, "fact3") == 0)   ||
  135.        (stricmp(level.mapname, "biggun") == 0)  ||
  136.        (stricmp(level.mapname, "space") == 0)   ||
  137.        (stricmp(level.mapname, "command") == 0) ||
  138.        (stricmp(level.mapname, "power2") == 0) ||
  139.        (stricmp(level.mapname, "strike") == 0))
  140.     {
  141.         // invoke one of our gross, ugly, disgusting hacks
  142.         self->think = SP_FixCoopSpots;
  143.         self->nextthink = level.time + FRAMETIME;
  144.     }
  145. }
  146.  
  147.  
  148. /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
  149. The deathmatch intermission point will be at one of these
  150. Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw.  'pitch yaw roll'
  151. */
  152. void SP_info_player_intermission(void)
  153. {
  154. }
  155.  
  156.  
  157. //=======================================================================
  158.  
  159.  
  160. void player_pain (edict_t *self, edict_t *other, float kick, int damage)
  161. {
  162.     // player pain is handled at the end of the frame in P_DamageFeedback
  163. }
  164.  
  165.  
  166. qboolean IsFemale (edict_t *ent)
  167. {
  168.     char        *info;
  169.  
  170.     if (!ent->client)
  171.         return false;
  172.  
  173.     info = Info_ValueForKey (ent->client->pers.userinfo, "skin");
  174.     if (info[0] == 'f' || info[0] == 'F')
  175.         return true;
  176.     return false;
  177. }
  178.  
  179.  
  180. void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker)
  181. {
  182.     int            mod;
  183.     char        *message;
  184.     char        *message2;
  185.     qboolean    ff;
  186.  
  187.     if (coop->value && attacker->client)
  188.         meansOfDeath |= MOD_FRIENDLY_FIRE;
  189.  
  190.     if (deathmatch->value || coop->value)
  191.     {
  192.         ff = meansOfDeath & MOD_FRIENDLY_FIRE;
  193.         mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
  194.         message = NULL;
  195.         message2 = "";
  196.  
  197.         switch (mod)
  198.         {
  199.         case MOD_SUICIDE:
  200.             message = "suicides";
  201.             break;
  202.         case MOD_FALLING:
  203.             message = "cratered";
  204.             break;
  205.         case MOD_CRUSH:
  206.             message = "was squished";
  207.             break;
  208.         case MOD_WATER:
  209.             message = "sank like a rock";
  210.             break;
  211.         case MOD_SLIME:
  212.             message = "melted";
  213.             break;
  214.         case MOD_LAVA:
  215.             message = "does a back flip into the lava";
  216.             break;
  217.         case MOD_EXPLOSIVE:
  218.         case MOD_BARREL:
  219.             message = "blew up";
  220.             break;
  221.         case MOD_EXIT:
  222.             message = "found a way out";
  223.             break;
  224.         case MOD_TARGET_LASER:
  225.             message = "saw the light";
  226.             break;
  227.         case MOD_TARGET_BLASTER:
  228.             message = "got blasted";
  229.             break;
  230.         case MOD_BOMB:
  231.         case MOD_SPLASH:
  232.         case MOD_TRIGGER_HURT:
  233.             message = "was in the wrong place";
  234.             break;
  235.         }
  236.         if (attacker == self)
  237.         {
  238.             switch (mod)
  239.             {
  240.             case MOD_HELD_GRENADE:
  241.                 message = "tried to put the pin back in";
  242.                 break;
  243.             case MOD_HG_SPLASH:
  244.             case MOD_G_SPLASH:
  245.                 if (IsFemale(self))
  246.                     message = "tripped on her own grenade";
  247.                 else
  248.                     message = "tripped on his own grenade";
  249.                 break;
  250.             case MOD_R_SPLASH:
  251.                 if (IsFemale(self))
  252.                     message = "blew herself up";
  253.                 else
  254.                     message = "blew himself up";
  255.                 break;
  256.             case MOD_BFG_BLAST:
  257.                 message = "should have used a smaller gun";
  258.                 break;
  259.             default:
  260.                 if (IsFemale(self))
  261.                     message = "killed herself";
  262.                 else
  263.                     message = "killed himself";
  264.                 break;
  265.             }
  266.         }
  267.         if (message)
  268.         {
  269.             gi.bprintf (PRINT_MEDIUM, "%s %s.\n", self->client->pers.netname, message);
  270.             if (deathmatch->value)
  271.                 self->client->resp.score--;
  272.             self->enemy = NULL;
  273.             return;
  274.         }
  275.  
  276.         self->enemy = attacker;
  277.         if (attacker && attacker->client)
  278.         {
  279.             switch (mod)
  280.             {
  281.             case MOD_BLASTER:
  282.                 message = "was blasted by";
  283.                 break;
  284.             case MOD_SHOTGUN:
  285.                 message = "was gunned down by";
  286.                 break;
  287.             case MOD_SSHOTGUN:
  288.                 message = "was blown away by";
  289.                 message2 = "'s super shotgun";
  290.                 break;
  291.             case MOD_MACHINEGUN:
  292.                 message = "was machinegunned by";
  293.                 break;
  294.             case MOD_CHAINGUN:
  295.                 message = "was cut in half by";
  296.                 message2 = "'s chaingun";
  297.                 break;
  298.             case MOD_GRENADE:
  299.                 message = "was popped by";
  300.                 message2 = "'s grenade";
  301.                 break;
  302.             case MOD_G_SPLASH:
  303.                 message = "was shredded by";
  304.                 message2 = "'s shrapnel";
  305.                 break;
  306.             case MOD_ROCKET:
  307.                 message = "ate";
  308.                 message2 = "'s rocket";
  309.                 break;
  310.             case MOD_R_SPLASH:
  311.                 message = "almost dodged";
  312.                 message2 = "'s rocket";
  313.                 break;
  314.             case MOD_HYPERBLASTER:
  315.                 message = "was melted by";
  316.                 message2 = "'s phaser";
  317.                 break;
  318.             case MOD_RAILGUN:
  319.                 message = "was railed by";
  320.                 break;
  321.             case MOD_BFG_LASER:
  322.                 message = "saw the pretty lights from";
  323.                 message2 = "'s BFG";
  324.                 break;
  325.             case MOD_BFG_BLAST:
  326.                 message = "was disintegrated by";
  327.                 message2 = "'s BFG blast";
  328.                 break;
  329.             case MOD_BFG_EFFECT:
  330.                 message = "couldn't hide from";
  331.                 message2 = "'s BFG";
  332.                 break;
  333.             case MOD_HANDGRENADE:
  334.                 message = "caught";
  335.                 message2 = "'s handgrenade";
  336.                 break;
  337.             case MOD_HG_SPLASH:
  338.                 message = "didn't see";
  339.                 message2 = "'s handgrenade";
  340.                 break;
  341.             case MOD_HELD_GRENADE:
  342.                 message = "feels";
  343.                 message2 = "'s pain";
  344.                 break;
  345.             case MOD_TELEFRAG:
  346.                 message = "tried to invade";
  347.                 message2 = "'s personal space";
  348.                 break;
  349.             }
  350.             if (message)
  351.             {
  352.                 gi.bprintf (PRINT_MEDIUM,"%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2);
  353.                 if (deathmatch->value)
  354.                 {
  355.                     if (ff)
  356.                         attacker->client->resp.score--;
  357.                     else
  358.                         attacker->client->resp.score++;
  359.                 }
  360.                 return;
  361.             }
  362.         }
  363.     }
  364.  
  365.     gi.bprintf (PRINT_MEDIUM,"%s died.\n", self->client->pers.netname);
  366.     if (deathmatch->value)
  367.         self->client->resp.score--;
  368. }
  369.  
  370.  
  371. void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
  372.  
  373. void TossClientWeapon (edict_t *self)
  374. {
  375.     gitem_t        *item;
  376.     edict_t        *drop;
  377.     qboolean    quad;
  378.     float        spread;
  379.  
  380.     if (!deathmatch->value)
  381.         return;
  382.  
  383.     item = self->client->pers.weapon;
  384.     if (! self->client->pers.inventory[self->client->ammo_index] )
  385.         item = NULL;
  386.     if (item && (strcmp (item->pickup_name, "Blaster") == 0))
  387.         item = NULL;
  388.  
  389.     if (!((int)(dmflags->value) & DF_QUAD_DROP))
  390.         quad = false;
  391.     else
  392.         quad = (self->client->quad_framenum > (level.framenum + 10));
  393.  
  394.     if (item && quad)
  395.         spread = 22.5;
  396.     else
  397.         spread = 0.0;
  398.  
  399.     if (item)
  400.     {
  401.         self->client->v_angle[YAW] -= spread;
  402.         drop = Drop_Item (self, item);
  403.         self->client->v_angle[YAW] += spread;
  404.         drop->spawnflags = DROPPED_PLAYER_ITEM;
  405.     }
  406.  
  407.     if (quad)
  408.     {
  409.         self->client->v_angle[YAW] += spread;
  410.         drop = Drop_Item (self, FindItemByClassname ("item_quad"));
  411.         self->client->v_angle[YAW] -= spread;
  412.         drop->spawnflags |= DROPPED_PLAYER_ITEM;
  413.  
  414.         drop->touch = Touch_Item;
  415.         drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME;
  416.         drop->think = G_FreeEdict;
  417.     }
  418. }
  419.  
  420.  
  421. /*
  422. ==================
  423. LookAtKiller
  424. ==================
  425. */
  426. void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker)
  427. {
  428.     vec3_t        dir;
  429.  
  430.     if (attacker && attacker != world && attacker != self)
  431.     {
  432.         VectorSubtract (attacker->s.origin, self->s.origin, dir);
  433.     }
  434.     else if (inflictor && inflictor != world && inflictor != self)
  435.     {
  436.         VectorSubtract (inflictor->s.origin, self->s.origin, dir);
  437.     }
  438.     else
  439.     {
  440.         self->client->killer_yaw = self->s.angles[YAW];
  441.         return;
  442.     }
  443.  
  444.     self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]);
  445. }
  446.  
  447. /*
  448. ==================
  449. player_die
  450. ==================
  451. */
  452. void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  453. {
  454.     int        n;
  455.  
  456.     VectorClear (self->avelocity);
  457.  
  458.     self->takedamage = DAMAGE_YES;
  459.     self->movetype = MOVETYPE_TOSS;
  460.  
  461.     self->s.modelindex2 = 0;    // remove linked weapon model
  462.     self->s.modelindex3 = 0;    //remove helmet, if a martian
  463.  
  464.     self->s.angles[0] = 0;
  465.     self->s.angles[2] = 0;
  466.  
  467.     self->s.sound = 0;
  468.     self->client->weapon_sound = 0;
  469.  
  470.     self->maxs[2] = -8;
  471.  
  472. //    self->solid = SOLID_NOT;
  473.     self->svflags |= SVF_DEADMONSTER;
  474.  
  475.     if (!self->deadflag)
  476.     {
  477.         self->client->respawn_time = level.time + 1.0;
  478.         LookAtKiller (self, inflictor, attacker);
  479.         self->client->ps.pmove.pm_type = PM_DEAD;
  480.         ClientObituary (self, inflictor, attacker);
  481.         TossClientWeapon (self);
  482.         if (deathmatch->value)
  483.             Cmd_Help_f (self);        // show scores
  484.     }
  485.  
  486.     // remove powerups
  487.     self->client->quad_framenum = 0;
  488.     self->client->invincible_framenum = 0;
  489.     self->client->breather_framenum = 0;
  490.     self->client->enviro_framenum = 0;
  491.  
  492.     // clear inventory
  493.     memset(self->client->pers.inventory, 0, sizeof(self->client->pers.inventory));
  494.  
  495.     if (self->health < -40)
  496.     {    // gib
  497.         gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  498.         for (n= 0; n < 4; n++)
  499.             ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC, EF_GIB);
  500.         ThrowClientHead (self, damage);
  501.  
  502.         self->takedamage = DAMAGE_NO;
  503.     }
  504.     else
  505.     {    // normal death
  506.         if (!self->deadflag)
  507.         {
  508.             static int i;
  509.  
  510.             i = (i+1)%3;
  511.             // start a death animation
  512.             self->client->anim_priority = ANIM_DEATH;
  513.             if (self->client->ps.pmove.pm_flags & PMF_DUCKED)
  514.             {
  515.                 self->s.frame = FRAME_crdeath1-1;
  516.                 self->client->anim_end = FRAME_crdeath5;
  517.             }
  518.             else switch (i)
  519.             {
  520.             case 0:
  521.                 self->s.frame = FRAME_death101-1;
  522.                 self->client->anim_end = FRAME_death106;
  523.                 break;
  524.             case 1:
  525.                 self->s.frame = FRAME_death201-1;
  526.                 self->client->anim_end = FRAME_death206;
  527.                 break;
  528.             case 2:
  529.                 self->s.frame = FRAME_death301-1;
  530.                 self->client->anim_end = FRAME_death308;
  531.                 break;
  532.             }
  533.             gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
  534.         }
  535.     }
  536.  
  537.     self->deadflag = DEAD_DEAD;
  538.  
  539.     gi.linkentity (self);
  540. }
  541.  
  542. //=======================================================================
  543.  
  544. /*
  545. ==============
  546. InitClientPersistant
  547.  
  548. This is only called when the game first initializes in single player,
  549. but is called after each death and level change in deathmatch
  550. ==============
  551. */
  552. void InitClientPersistant (gclient_t *client)
  553. {
  554.     gitem_t        *item;
  555.  
  556.     memset (&client->pers, 0, sizeof(client->pers));
  557.  
  558.     item = FindItem("Blaster");
  559.     client->pers.selected_item = ITEM_INDEX(item);
  560.     client->pers.inventory[client->pers.selected_item] = 1;
  561.  
  562.     client->pers.weapon = item;
  563.  
  564.     client->pers.health            = 100;
  565.     client->pers.max_health        = 100;
  566.  
  567.     client->pers.max_bullets    = 200;
  568.     client->pers.max_shells        = 100;
  569.     client->pers.max_rockets    = 50;
  570.     client->pers.max_grenades    = 50;
  571.     client->pers.max_cells        = 200;
  572.     client->pers.max_slugs        = 50;
  573.  
  574.     client->pers.connected = true;
  575. }
  576.  
  577.  
  578. void InitClientResp (gclient_t *client)
  579. {
  580.     memset (&client->resp, 0, sizeof(client->resp));
  581.     client->resp.enterframe = level.framenum;
  582.     client->resp.coop_respawn = client->pers;
  583. }
  584.  
  585. /*
  586. ==================
  587. SaveClientData
  588.  
  589. Some information that should be persistant, like health, 
  590. is still stored in the edict structure, so it needs to
  591. be mirrored out to the client structure before all the
  592. edicts are wiped.
  593. ==================
  594. */
  595. void SaveClientData (void)
  596. {
  597.     int        i;
  598.     edict_t    *ent;
  599.  
  600.     for (i=0 ; i<game.maxclients ; i++)
  601.     {
  602.         ent = &g_edicts[1+i];
  603.         if (!ent->inuse)
  604.             continue;
  605.         game.clients[i].pers.health = ent->health;
  606.         game.clients[i].pers.max_health = ent->max_health;
  607. //        game.clients[i].pers.powerArmorActive = (ent->flags & FL_POWER_ARMOR);
  608.         if (coop->value)
  609.             game.clients[i].pers.score = ent->client->resp.score;
  610.     }
  611. }
  612.  
  613. void FetchClientEntData (edict_t *ent)
  614. {
  615.     ent->health = ent->client->pers.health;
  616.     ent->max_health = ent->client->pers.max_health;
  617. //    if (ent->client->pers.powerArmorActive)
  618.         ent->flags |= FL_POWER_ARMOR;
  619.     if (coop->value)
  620.         ent->client->resp.score = ent->client->pers.score;
  621. }
  622.  
  623.  
  624.  
  625. /*
  626. =======================================================================
  627.  
  628.   SelectSpawnPoint
  629.  
  630. =======================================================================
  631. */
  632.  
  633. /*
  634. ================
  635. PlayersRangeFromSpot
  636.  
  637. Returns the distance to the nearest player from the given spot
  638. ================
  639. */
  640. float    PlayersRangeFromSpot (edict_t *spot)
  641. {
  642.     edict_t    *player;
  643.     float    bestplayerdistance;
  644.     vec3_t    v;
  645.     int        n;
  646.     float    playerdistance;
  647.  
  648.  
  649.     bestplayerdistance = 9999999;
  650.  
  651.     for (n = 1; n <= maxclients->value; n++)
  652.     {
  653.         player = &g_edicts[n];
  654.  
  655.         if (!player->inuse)
  656.             continue;
  657.  
  658.         if (player->health <= 0)
  659.             continue;
  660.  
  661.         VectorSubtract (spot->s.origin, player->s.origin, v);
  662.         playerdistance = VectorLength (v);
  663.  
  664.         if (playerdistance < bestplayerdistance)
  665.             bestplayerdistance = playerdistance;
  666.     }
  667.  
  668.     return bestplayerdistance;
  669. }
  670.  
  671. /*
  672. ================
  673. SelectRandomDeathmatchSpawnPoint
  674.  
  675. go to a random point, but NOT the two points closest
  676. to other players
  677. ================
  678. */
  679. edict_t *SelectRandomDeathmatchSpawnPoint (void)
  680. {
  681.     edict_t    *spot, *spot1, *spot2;
  682.     int        count = 0;
  683.     int        selection;
  684.     float    range, range1, range2;
  685.  
  686.     spot = NULL;
  687.     range1 = range2 = 99999;
  688.     spot1 = spot2 = NULL;
  689.  
  690.     while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
  691.     {
  692.         count++;
  693.         range = PlayersRangeFromSpot(spot);
  694.         if (range < range1)
  695.         {
  696.             range1 = range;
  697.             spot1 = spot;
  698.         }
  699.         else if (range < range2)
  700.         {
  701.             range2 = range;
  702.             spot2 = spot;
  703.         }
  704.     }
  705.  
  706.     if (!count)
  707.         return NULL;
  708.  
  709.     if (count <= 2)
  710.     {
  711.         spot1 = spot2 = NULL;
  712.     }
  713.     else
  714.     {
  715.         if ( spot1 ) {
  716.             count--;
  717.         }
  718.         if ( spot2 ) {
  719.             count--;
  720.         }
  721.     }
  722.  
  723.     selection = rand() % count;
  724.  
  725.     spot = NULL;
  726.     do
  727.     {
  728.         spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
  729.         if (spot == spot1 || spot == spot2)
  730.             selection++;
  731.     } while(selection--);
  732.  
  733.     return spot;
  734. }
  735.  
  736. /*
  737. ================
  738. SelectFarthestDeathmatchSpawnPoint
  739.  
  740. ================
  741. */
  742. edict_t *SelectFarthestDeathmatchSpawnPoint (void)
  743. {
  744.     edict_t    *bestspot;
  745.     float    bestdistance, bestplayerdistance;
  746.     edict_t    *spot;
  747.  
  748.  
  749.     spot = NULL;
  750.     bestspot = NULL;
  751.     bestdistance = 0;
  752.     while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL)
  753.     {
  754.         bestplayerdistance = PlayersRangeFromSpot (spot);
  755.  
  756.         if (bestplayerdistance > bestdistance)
  757.         {
  758.             bestspot = spot;
  759.             bestdistance = bestplayerdistance;
  760.         }
  761.     }
  762.  
  763.     if (bestspot)
  764.     {
  765.         return bestspot;
  766.     }
  767.  
  768.     // if there is a player just spawned on each and every start spot
  769.     // we have no choice to turn one into a telefrag meltdown
  770.     spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
  771.  
  772.     return spot;
  773. }
  774.  
  775. edict_t *SelectDeathmatchSpawnPoint (void)
  776. {
  777.     if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
  778.         return SelectFarthestDeathmatchSpawnPoint ();
  779.     else
  780.         return SelectRandomDeathmatchSpawnPoint ();
  781. }
  782.  
  783.  
  784. edict_t *SelectCoopSpawnPoint (edict_t *ent)
  785. {
  786.     int        index;
  787.     edict_t    *spot = NULL;
  788.     char    *target;
  789.  
  790.     index = ent->client - game.clients;
  791.  
  792.     // player 0 starts in normal player spawn point
  793.     if (!index)
  794.         return NULL;
  795.  
  796.     spot = NULL;
  797.  
  798.     // assume there are four coop spots at each spawnpoint
  799.     while (1)
  800.     {
  801.         spot = G_Find (spot, FOFS(classname), "info_player_coop");
  802.         if (!spot)
  803.             return NULL;    // we didn't have enough...
  804.  
  805.         target = spot->targetname;
  806.         if (!target)
  807.             target = "";
  808.         if ( Q_stricmp(game.spawnpoint, target) == 0 )
  809.         {    // this is a coop spawn point for one of the clients here
  810.             index--;
  811.             if (!index)
  812.                 return spot;        // this is it
  813.         }
  814.     }
  815.  
  816.  
  817.     return spot;
  818. }
  819.  
  820.  
  821. /*
  822. ===========
  823. SelectSpawnPoint
  824.  
  825. Chooses a player start, deathmatch start, coop start, etc
  826. ============
  827. */
  828. void    SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
  829. {
  830.     edict_t    *spot = NULL;
  831.  
  832.     if (deathmatch->value)
  833.         spot = SelectDeathmatchSpawnPoint ();
  834.     else if (coop->value)
  835.         spot = SelectCoopSpawnPoint (ent);
  836.  
  837.     // find a single player start spot
  838.     if (!spot)
  839.     {
  840.         while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL)
  841.         {
  842.             if (!game.spawnpoint[0] && !spot->targetname)
  843.                 break;
  844.  
  845.             if (!game.spawnpoint[0] || !spot->targetname)
  846.                 continue;
  847.  
  848.             if (Q_stricmp(game.spawnpoint, spot->targetname) == 0)
  849.                 break;
  850.         }
  851.  
  852.         if (!spot)
  853.         {
  854.             if (!game.spawnpoint[0])
  855.             {    // there wasn't a spawnpoint without a target, so use any
  856.                 spot = G_Find (spot, FOFS(classname), "info_player_start");
  857.             }
  858.             if (!spot)
  859.                 gi.error ("Couldn't find spawn point %s\n", game.spawnpoint);
  860.         }
  861.     }
  862.  
  863.     VectorCopy (spot->s.origin, origin);
  864.     origin[2] += 9;
  865.     VectorCopy (spot->s.angles, angles);
  866. }
  867.  
  868. //======================================================================
  869.  
  870.  
  871. void InitBodyQue (void)
  872. {
  873.     int        i;
  874.     edict_t    *ent;
  875.  
  876.     level.body_que = 0;
  877.     for (i=0; i<BODY_QUEUE_SIZE ; i++)
  878.     {
  879.         ent = G_Spawn();
  880.         ent->classname = "bodyque";
  881.     }
  882. }
  883.  
  884. void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  885. {
  886.     int    n;
  887.  
  888.     if (self->health < -40)
  889.     {
  890.         gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  891.         for (n= 0; n < 4; n++)
  892.             ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC, EF_GIB);
  893.         self->s.origin[2] -= 48;
  894.         ThrowClientHead (self, damage);
  895.         self->takedamage = DAMAGE_NO;
  896.     }
  897. }
  898.  
  899. void CopyToBodyQue (edict_t *ent)
  900. {
  901.     edict_t        *body;
  902.  
  903.     // grab a body que and cycle to the next one
  904.     body = &g_edicts[(int)maxclients->value + level.body_que + 1];
  905.     level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE;
  906.  
  907.     // FIXME: send an effect on the removed body
  908.  
  909.     gi.unlinkentity (ent);
  910.  
  911.     gi.unlinkentity (body);
  912.     body->s = ent->s;
  913.     body->s.number = body - g_edicts;
  914.  
  915.     body->svflags = ent->svflags;
  916.     VectorCopy (ent->mins, body->mins);
  917.     VectorCopy (ent->maxs, body->maxs);
  918.     VectorCopy (ent->absmin, body->absmin);
  919.     VectorCopy (ent->absmax, body->absmax);
  920.     VectorCopy (ent->size, body->size);
  921.     body->solid = ent->solid;
  922.     body->clipmask = ent->clipmask;
  923.     body->owner = ent->owner;
  924.     body->movetype = ent->movetype;
  925.  
  926.     body->die = body_die;
  927.     body->takedamage = DAMAGE_YES;
  928.  
  929.     gi.linkentity (body);
  930. }
  931.  
  932.  
  933. void respawn (edict_t *self)
  934. {
  935.     if (deathmatch->value || coop->value)
  936.     {
  937.         CopyToBodyQue (self);
  938.         PutClientInServer (self);
  939.  
  940.         // add a teleportation effect
  941.         self->s.event = EV_PLAYER_TELEPORT;
  942.  
  943.         // hold in place briefly
  944.         self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
  945.         self->client->ps.pmove.pm_time = 14;
  946.  
  947.         self->client->respawn_time = level.time;
  948.  
  949.         return;
  950.     }
  951.  
  952.     // restart the entire server
  953.     gi.AddCommandString ("menu_loadgame\n");
  954. }
  955.  
  956. //==============================================================
  957.  
  958.  
  959. /*
  960. ===========
  961. PutClientInServer
  962.  
  963. Called when a player connects to a server or respawns in
  964. a deathmatch.
  965. ============
  966. */
  967. void PutClientInServer (edict_t *ent)
  968. {
  969.     vec3_t    mins = {-16, -16, -24};
  970.     vec3_t    maxs = {16, 16, 32};
  971.     int        index;
  972.     vec3_t    spawn_origin, spawn_angles;
  973.     gclient_t    *client;
  974.     int        i;
  975.     client_persistant_t    saved;
  976.     client_respawn_t    resp;
  977.     char        *info;
  978.     // find a spawn point
  979.     // do it before setting health back up, so farthest
  980.     // ranging doesn't count this client
  981.     SelectSpawnPoint (ent, spawn_origin, spawn_angles);
  982.  
  983.     index = ent-g_edicts-1;
  984.     client = ent->client;
  985.  
  986.     // deathmatch wipes most client data every spawn
  987.     if (deathmatch->value)
  988.     {
  989.         char        userinfo[MAX_INFO_STRING];
  990.  
  991.         resp = client->resp;
  992.         memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
  993.         InitClientPersistant (client);
  994.         ClientUserinfoChanged (ent, userinfo);
  995.     }
  996.     else if (coop->value)
  997.     {
  998.         int            n;
  999.         char        userinfo[MAX_INFO_STRING];
  1000.  
  1001.         resp = client->resp;
  1002.         memcpy (userinfo, client->pers.userinfo, sizeof(userinfo));
  1003.         // this is kind of ugly, but it's how we want to handle keys in coop
  1004.         for (n = 0; n < MAX_ITEMS; n++)
  1005.         {
  1006.             if (itemlist[n].flags & IT_KEY)
  1007.                 resp.coop_respawn.inventory[n] = client->pers.inventory[n];
  1008.         }
  1009.         client->pers = resp.coop_respawn;
  1010.         ClientUserinfoChanged (ent, userinfo);
  1011.         if (resp.score > client->pers.score)
  1012.             client->pers.score = resp.score;
  1013.     }
  1014.     else
  1015.     {
  1016.         memset (&resp, 0, sizeof(resp));
  1017.     }
  1018.  
  1019.     // clear everything but the persistant data
  1020.     saved = client->pers;
  1021.     memset (client, 0, sizeof(*client));
  1022.     client->pers = saved;
  1023.     if (client->pers.health <= 0)
  1024.         InitClientPersistant(client);
  1025.     client->resp = resp;
  1026.  
  1027.     // copy some data from the client to the entity
  1028.     FetchClientEntData (ent);
  1029.  
  1030.     // clear entity values
  1031.     ent->groundentity = NULL;
  1032.     ent->client = &game.clients[index];
  1033.     ent->takedamage = DAMAGE_AIM;
  1034.     ent->movetype = MOVETYPE_WALK;
  1035.     ent->viewheight = 22;
  1036.     ent->inuse = true;
  1037.     ent->classname = "player";
  1038.     ent->mass = 200;
  1039.     ent->solid = SOLID_BBOX;
  1040.     ent->deadflag = DEAD_NO;
  1041.     ent->air_finished = level.time + 12;
  1042.     ent->clipmask = MASK_PLAYERSOLID;
  1043.     ent->model = "players/male/tris.md2";
  1044.     ent->pain = player_pain;
  1045.     ent->die = player_die;
  1046.     ent->waterlevel = 0;
  1047.     ent->watertype = 0;
  1048.     ent->flags &= ~FL_NO_KNOCKBACK;
  1049.     ent->svflags &= ~SVF_DEADMONSTER;
  1050.  
  1051.     VectorCopy (mins, ent->mins);
  1052.     VectorCopy (maxs, ent->maxs);
  1053.     VectorClear (ent->velocity);
  1054.  
  1055.     // clear playerstate values
  1056.     memset (&ent->client->ps, 0, sizeof(client->ps));
  1057.  
  1058.     client->ps.pmove.origin[0] = spawn_origin[0]*8;
  1059.     client->ps.pmove.origin[1] = spawn_origin[1]*8;
  1060.     client->ps.pmove.origin[2] = spawn_origin[2]*8;
  1061.  
  1062.     if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
  1063.     {
  1064.         client->ps.fov = 90;
  1065.     }
  1066.     else
  1067.     {
  1068.         client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov"));
  1069.         if (client->ps.fov < 1)
  1070.             client->ps.fov = 90;
  1071.         else if (client->ps.fov > 160)
  1072.             client->ps.fov = 160;
  1073.     }
  1074.  
  1075.     client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
  1076.  
  1077.     // clear entity state values
  1078.     ent->s.effects = 0;
  1079.     ent->s.skinnum = ent - g_edicts - 1;
  1080.     ent->s.modelindex = 255;        // will use the skin specified model
  1081.     ent->s.modelindex2 = 255;        // custom gun model
  1082.     info = Info_ValueForKey (ent->client->pers.userinfo, "skin");
  1083.     if(info[0] == 'M' || info[0] == 'm')
  1084.         ent->s.modelindex3 = gi.modelindex("players/martian/helmet.md2");
  1085.     else 
  1086.         ent->s.modelindex3 = 0;
  1087.     ent->s.frame = 0;
  1088.     VectorCopy (spawn_origin, ent->s.origin);
  1089.     ent->s.origin[2] += 1;    // make sure off ground
  1090.     VectorCopy (ent->s.origin, ent->s.old_origin);
  1091.  
  1092.     // set the delta angle
  1093.     for (i=0 ; i<3 ; i++)
  1094.         client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]);
  1095.  
  1096.     ent->s.angles[PITCH] = 0;
  1097.     ent->s.angles[YAW] = spawn_angles[YAW];
  1098.     ent->s.angles[ROLL] = 0;
  1099.     VectorCopy (ent->s.angles, client->ps.viewangles);
  1100.     VectorCopy (ent->s.angles, client->v_angle);
  1101.  
  1102.     if (!KillBox (ent))
  1103.     {    // could't spawn in?
  1104.     }
  1105.  
  1106.     gi.linkentity (ent);
  1107.  
  1108.     // force the current weapon up
  1109.     client->newweapon = client->pers.weapon;
  1110.     ChangeWeapon (ent);
  1111. }
  1112.  
  1113. /*
  1114. =====================
  1115. ClientBeginDeathmatch
  1116.  
  1117. A client has just connected to the server in 
  1118. deathmatch mode, so clear everything out before starting them.
  1119. =====================
  1120. */
  1121. void ClientBeginDeathmatch (edict_t *ent)
  1122. {
  1123.     G_InitEdict (ent);
  1124.  
  1125.     InitClientResp (ent->client);
  1126.  
  1127.     // locate ent at a spawn point
  1128.     PutClientInServer (ent);
  1129.  
  1130.     // send effect
  1131.     gi.WriteByte (svc_muzzleflash);
  1132.     gi.WriteShort (ent-g_edicts);
  1133.     gi.WriteByte (MZ_LOGIN);
  1134.     gi.multicast (ent->s.origin, MULTICAST_PVS);
  1135.  
  1136.     gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
  1137.  
  1138.     // make sure all view stuff is valid
  1139.     ClientEndServerFrame (ent);
  1140. }
  1141.  
  1142.  
  1143. /*
  1144. ===========
  1145. ClientBegin
  1146.  
  1147. called when a client has finished connecting, and is ready
  1148. to be placed into the game.  This will happen every level load.
  1149. ============
  1150. */
  1151. void ClientBegin (edict_t *ent)
  1152. {
  1153.     int        i;
  1154.  
  1155.     ent->client = game.clients + (ent - g_edicts - 1);
  1156.  
  1157.     if (deathmatch->value)
  1158.     {
  1159.         ClientBeginDeathmatch (ent);
  1160.         return;
  1161.     }
  1162.  
  1163.     // if there is already a body waiting for us (a loadgame), just
  1164.     // take it, otherwise spawn one from scratch
  1165.     if (ent->inuse == true)
  1166.     {
  1167.         // the client has cleared the client side viewangles upon
  1168.         // connecting to the server, which is different than the
  1169.         // state when the game is saved, so we need to compensate
  1170.         // with deltaangles
  1171.         for (i=0 ; i<3 ; i++)
  1172.             ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]);
  1173.     }
  1174.     else
  1175.     {
  1176.         // a spawn point will completely reinitialize the entity
  1177.         // except for the persistant data that was initialized at
  1178.         // ClientConnect() time
  1179.         G_InitEdict (ent);
  1180.         ent->classname = "player";
  1181.         InitClientResp (ent->client);
  1182.         PutClientInServer (ent);
  1183.     }
  1184.  
  1185.     if (level.intermissiontime)
  1186.     {
  1187.         MoveClientToIntermission (ent);
  1188.     }
  1189.     else
  1190.     {
  1191.         // send effect if in a multiplayer game
  1192.         if (game.maxclients > 1)
  1193.         {
  1194.             gi.WriteByte (svc_muzzleflash);
  1195.             gi.WriteShort (ent-g_edicts);
  1196.             gi.WriteByte (MZ_LOGIN);
  1197.             gi.multicast (ent->s.origin, MULTICAST_PVS);
  1198.  
  1199.             gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
  1200.         }
  1201.     }
  1202.  
  1203.     // make sure all view stuff is valid
  1204.     ClientEndServerFrame (ent);
  1205. }
  1206.  
  1207. /*
  1208. ===========
  1209. ClientUserInfoChanged
  1210.  
  1211. called whenever the player updates a userinfo variable.
  1212.  
  1213. The game can override any of the settings in place
  1214. (forcing skins or names, etc) before copying it off.
  1215. ============
  1216. */
  1217. void ClientUserinfoChanged (edict_t *ent, char *userinfo)
  1218. {
  1219.     char    *s;
  1220.     char    *info;
  1221.     int        playernum;
  1222.  
  1223.     // check for malformed or illegal info strings
  1224.     if (!Info_Validate(userinfo))
  1225.     {
  1226.         strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt");
  1227.     }
  1228.  
  1229.     // set name
  1230.     s = Info_ValueForKey (userinfo, "name");
  1231.     strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1);
  1232.  
  1233.     // set skin
  1234.     s = Info_ValueForKey (userinfo, "skin");
  1235.  
  1236.     playernum = ent-g_edicts-1;
  1237.  
  1238.     // combine name and skin into a configstring
  1239.     gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) );
  1240.  
  1241.     info = Info_ValueForKey (ent->client->pers.userinfo, "skin");
  1242.     if(info[0] != 'M' && info[0] != 'm')
  1243.         ent->s.modelindex3 = gi.modelindex("players/martian/helmet.md2");
  1244.     else 
  1245.         ent->s.modelindex3 = 0; //don't want anything other than a martian having a helmet.
  1246.  
  1247.     // fov
  1248.     if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV))
  1249.     {
  1250.         ent->client->ps.fov = 90;
  1251.     }
  1252.     else
  1253.     {
  1254.         ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
  1255.         if (ent->client->ps.fov < 1)
  1256.             ent->client->ps.fov = 90;
  1257.         else if (ent->client->ps.fov > 160)
  1258.             ent->client->ps.fov = 160;
  1259.     }
  1260.  
  1261.     // handedness
  1262.     s = Info_ValueForKey (userinfo, "hand");
  1263.     if (strlen(s))
  1264.     {
  1265.         ent->client->pers.hand = atoi(s);
  1266.     }
  1267.  
  1268.     // save off the userinfo in case we want to check something later
  1269.     strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1);
  1270. }
  1271.  
  1272.  
  1273. /*
  1274. ===========
  1275. ClientConnect
  1276.  
  1277. Called when a player begins connecting to the server.
  1278. The game can refuse entrance to a client by returning false.
  1279. If the client is allowed, the connection process will continue
  1280. and eventually get to ClientBegin()
  1281. Changing levels will NOT cause this to be called again, but
  1282. loadgames will.
  1283. ============
  1284. */
  1285. qboolean ClientConnect (edict_t *ent, char *userinfo)
  1286. {
  1287.     char    *value;
  1288.  
  1289.     // check to see if they are on the banned IP list
  1290.     value = Info_ValueForKey (userinfo, "ip");
  1291.  
  1292.     // check for a password
  1293.     value = Info_ValueForKey (userinfo, "password");
  1294.     if (strcmp(password->string, value) != 0)
  1295.         return false;
  1296.  
  1297.     // they can connect
  1298.     ent->client = game.clients + (ent - g_edicts - 1);
  1299.  
  1300.     // if there is already a body waiting for us (a loadgame), just
  1301.     // take it, otherwise spawn one from scratch
  1302.     if (ent->inuse == false)
  1303.     {
  1304.         // clear the respawning variables
  1305.         InitClientResp (ent->client);
  1306.         if (!game.autosaved || !ent->client->pers.weapon)
  1307.             InitClientPersistant (ent->client);
  1308.     }
  1309.  
  1310.     ClientUserinfoChanged (ent, userinfo);
  1311.  
  1312.     if (game.maxclients > 1)
  1313.         gi.dprintf ("%s connected\n", ent->client->pers.netname);
  1314.  
  1315.     ent->client->pers.connected = true;
  1316.     return true;
  1317. }
  1318.  
  1319. /*
  1320. ===========
  1321. ClientDisconnect
  1322.  
  1323. Called when a player drops from the server.
  1324. Will not be called between levels.
  1325. ============
  1326. */
  1327. void ClientDisconnect (edict_t *ent)
  1328. {
  1329.     int        playernum;
  1330.  
  1331.     if (!ent->client)
  1332.         return;
  1333.  
  1334.     gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
  1335.  
  1336.     // send effect
  1337.     gi.WriteByte (svc_muzzleflash);
  1338.     gi.WriteShort (ent-g_edicts);
  1339.     gi.WriteByte (MZ_LOGOUT);
  1340.     gi.multicast (ent->s.origin, MULTICAST_PVS);
  1341.  
  1342.     gi.unlinkentity (ent);
  1343.     ent->s.modelindex = 0;
  1344.     ent->solid = SOLID_NOT;
  1345.     ent->inuse = false;
  1346.     ent->classname = "disconnected";
  1347.     ent->client->pers.connected = false;
  1348.  
  1349.     playernum = ent-g_edicts-1;
  1350.     gi.configstring (CS_PLAYERSKINS+playernum, "");
  1351. }
  1352.  
  1353.  
  1354. //==============================================================
  1355.  
  1356.  
  1357. edict_t    *pm_passent;
  1358.  
  1359. // pmove doesn't need to know about passent and contentmask
  1360. trace_t    PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
  1361. {
  1362.     if (pm_passent->health > 0)
  1363.         return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
  1364.     else
  1365.         return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID);
  1366. }
  1367.  
  1368. unsigned CheckBlock (void *b, int c)
  1369. {
  1370.     int    v,i;
  1371.     v = 0;
  1372.     for (i=0 ; i<c ; i++)
  1373.         v+= ((byte *)b)[i];
  1374.     return v;
  1375. }
  1376. void PrintPmove (pmove_t *pm)
  1377. {
  1378.     unsigned    c1, c2;
  1379.  
  1380.     c1 = CheckBlock (&pm->s, sizeof(pm->s));
  1381.     c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd));
  1382.     Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2);
  1383. }
  1384.  
  1385. /*
  1386. ==============
  1387. ClientThink
  1388.  
  1389. This will be called once for each client frame, which will
  1390. usually be a couple times for each server frame.
  1391. ==============
  1392. */
  1393. void ClientThink (edict_t *ent, usercmd_t *ucmd)
  1394. {
  1395.     gclient_t    *client;
  1396.     edict_t    *other;
  1397.     int        i, j;
  1398.     pmove_t    pm;
  1399.  
  1400.     level.current_entity = ent;
  1401.     client = ent->client;
  1402.  
  1403.     if (level.intermissiontime)
  1404.     {
  1405.         client->ps.pmove.pm_type = PM_FREEZE;
  1406.         // can exit intermission after five seconds
  1407.         if (level.time > level.intermissiontime + 5.0 
  1408.             && (ucmd->buttons & BUTTON_ANY) )
  1409.             level.exitintermission = true;
  1410.         return;
  1411.     }
  1412.  
  1413.     pm_passent = ent;
  1414.  
  1415.     // set up for pmove
  1416.     memset (&pm, 0, sizeof(pm));
  1417.  
  1418.     if (ent->movetype == MOVETYPE_NOCLIP)
  1419.         client->ps.pmove.pm_type = PM_SPECTATOR;
  1420.     else if (ent->s.modelindex != 255)
  1421.         client->ps.pmove.pm_type = PM_GIB;
  1422.     else if (ent->deadflag)
  1423.         client->ps.pmove.pm_type = PM_DEAD;
  1424.     else
  1425.         client->ps.pmove.pm_type = PM_NORMAL;
  1426.  
  1427.     client->ps.pmove.gravity = sv_gravity->value;
  1428.     pm.s = client->ps.pmove;
  1429.  
  1430.     for (i=0 ; i<3 ; i++)
  1431.     {
  1432.         pm.s.origin[i] = ent->s.origin[i]*8;
  1433.         pm.s.velocity[i] = ent->velocity[i]*8;
  1434.     }
  1435.  
  1436.     if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
  1437.     {
  1438.         pm.snapinitial = true;
  1439. //        gi.dprintf ("pmove changed!\n");
  1440.     }
  1441.  
  1442.     pm.cmd = *ucmd;
  1443.  
  1444.     pm.trace = PM_trace;    // adds default parms
  1445.     pm.pointcontents = gi.pointcontents;
  1446.  
  1447.     // perform a pmove
  1448.     gi.Pmove (&pm);
  1449.  
  1450.     // save results of pmove
  1451.     client->ps.pmove = pm.s;
  1452.     client->old_pmove = pm.s;
  1453.  
  1454.     for (i=0 ; i<3 ; i++)
  1455.     {
  1456.         ent->s.origin[i] = pm.s.origin[i]*0.125;
  1457.         ent->velocity[i] = pm.s.velocity[i]*0.125;
  1458.     }
  1459.  
  1460.     VectorCopy (pm.mins, ent->mins);
  1461.     VectorCopy (pm.maxs, ent->maxs);
  1462.  
  1463.     client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
  1464.     client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
  1465.     client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
  1466.  
  1467.     if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
  1468.     {
  1469.         gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
  1470.         PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
  1471.     }
  1472.  
  1473.     ent->viewheight = pm.viewheight;
  1474.     ent->waterlevel = pm.waterlevel;
  1475.     ent->watertype = pm.watertype;
  1476.     ent->groundentity = pm.groundentity;
  1477.     if (pm.groundentity)
  1478.         ent->groundentity_linkcount = pm.groundentity->linkcount;
  1479.  
  1480.     if (ent->deadflag)
  1481.     {
  1482.         client->ps.viewangles[ROLL] = 40;
  1483.         client->ps.viewangles[PITCH] = -15;
  1484.         client->ps.viewangles[YAW] = client->killer_yaw;
  1485.     }
  1486.     else
  1487.     {
  1488.         VectorCopy (pm.viewangles, client->v_angle);
  1489.         VectorCopy (pm.viewangles, client->ps.viewangles);
  1490.     }
  1491.  
  1492.  
  1493.     gi.linkentity (ent);
  1494.  
  1495.     if (ent->movetype != MOVETYPE_NOCLIP)
  1496.         G_TouchTriggers (ent);
  1497.  
  1498.     // touch other objects
  1499.     for (i=0 ; i<pm.numtouch ; i++)
  1500.     {
  1501.         other = pm.touchents[i];
  1502.         for (j=0 ; j<i ; j++)
  1503.             if (pm.touchents[j] == other)
  1504.                 break;
  1505.         if (j != i)
  1506.             continue;    // duplicated
  1507.         if (!other->touch)
  1508.             continue;
  1509.         other->touch (other, ent, NULL, NULL);
  1510.     }
  1511.  
  1512.  
  1513.     client->oldbuttons = client->buttons;
  1514.     client->buttons = ucmd->buttons;
  1515.     client->latched_buttons |= client->buttons & ~client->oldbuttons;
  1516.  
  1517.     // save light level the player is standing on for
  1518.     // monster sighting AI
  1519.     ent->light_level = ucmd->lightlevel;
  1520.  
  1521.     // fire weapon from final position if needed
  1522.     if (client->latched_buttons & BUTTON_ATTACK)
  1523.     {
  1524.         if (!client->weapon_thunk)
  1525.         {
  1526.             client->weapon_thunk = true;
  1527.             Think_Weapon (ent);
  1528.         }
  1529.     }
  1530.  
  1531.  
  1532. }
  1533.  
  1534.  
  1535. /*
  1536. ==============
  1537. ClientBeginServerFrame
  1538.  
  1539. This will be called once for each server frame, before running
  1540. any other entities in the world.
  1541. ==============
  1542. */
  1543. void ClientBeginServerFrame (edict_t *ent)
  1544. {
  1545.     gclient_t    *client;
  1546.     int            buttonMask;
  1547.  
  1548.     if (level.intermissiontime)
  1549.         return;
  1550.  
  1551.     client = ent->client;
  1552.  
  1553.     // run weapon animations if it hasn't been done by a ucmd_t
  1554.     if (!client->weapon_thunk)
  1555.         Think_Weapon (ent);
  1556.     else
  1557.         client->weapon_thunk = false;
  1558.  
  1559.     if (ent->deadflag)
  1560.     {
  1561.         // wait for any button just going down
  1562.         if ( level.time > client->respawn_time)
  1563.         {
  1564.             // in deathmatch, only wait for attack button
  1565.             if (deathmatch->value)
  1566.                 buttonMask = BUTTON_ATTACK;
  1567.             else
  1568.                 buttonMask = -1;
  1569.  
  1570.             if ( ( client->latched_buttons & buttonMask ) ||
  1571.                 (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) )
  1572.             {
  1573.                 respawn(ent);
  1574.                 client->latched_buttons = 0;
  1575.             }
  1576.         }
  1577.         return;
  1578.     }
  1579.  
  1580.     // add player trail so monsters can follow
  1581.     if (!deathmatch->value)
  1582.         if (!visible (ent, PlayerTrail_LastSpot() ) )
  1583.             PlayerTrail_Add (ent->s.old_origin);
  1584.  
  1585.     client->latched_buttons = 0;
  1586. }
  1587.